///////////////////////////////////////////////////////////////////////////////
//
// Copyright David Abrahams 2002, Joel de Guzman, 2002. Permission to copy,
// use, modify, sell and distribute this software is granted provided this
// copyright notice appears in all copies. This software is provided "as is"
// without express or implied warranty, and with no claim as to its
// suitability for any purpose.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef DEFAULTS_GEN_JDG20020807_HPP
#define DEFAULTS_GEN_JDG20020807_HPP

#include <boost/python/detail/preprocessor.hpp>
#include <boost/preprocessor/repeat.hpp>
#include <boost/preprocessor/repeat_from_to.hpp>
#include <boost/preprocessor/enum.hpp>
#include <boost/preprocessor/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/tuple.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/arithmetic/sub.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/inc.hpp>
#include <boost/preprocessor/empty.hpp>
#include <boost/preprocessor/comma_if.hpp>
#include <boost/config.hpp>
#include <boost/mpl/begin_end.hpp>
#include <boost/mpl/next.hpp>
#include <boost/mpl/apply.hpp>

namespace boost { namespace python {

namespace detail
{
  // overloads_base is used as a base class for all function
  // stubs. This class holds the doc_string of the stubs.
  struct overloads_base
  {
      overloads_base(char const* doc_)
          : m_doc(doc_) {}

      overloads_base(char const* doc_, detail::keyword_range const& kw)
          : m_doc(doc_), m_keywords(kw) {}

      char const* doc_string() const
      {
          return m_doc;
      }

      detail::keyword_range const& keywords() const
      {
          return m_keywords;
      }

   private:
      char const* m_doc;
      detail::keyword_range m_keywords;
  };

  // overloads_proxy is generated by the overloads_common operator[] (see
  // below). This class holds a user defined call policies of the stubs.
  template <class CallPoliciesT, class OverloadsT>
  struct overloads_proxy
      : public overloads_base
  {
      typedef typename OverloadsT::non_void_return_type   non_void_return_type;
      typedef typename OverloadsT::void_return_type       void_return_type;

      overloads_proxy(
          CallPoliciesT const& policies_
          , char const* doc
          , keyword_range const& kw
          )
          : overloads_base(doc, kw)
            , policies(policies_)
      {}

      CallPoliciesT
      call_policies() const
      {
          return policies;
      }

      CallPoliciesT policies;
  };

  // overloads_common is our default function stubs base class. This
  // class returns the default_call_policies in its call_policies()
  // member function.  It can generate a overloads_proxy however through
  // its operator[]
  template <class DerivedT>
  struct overloads_common
      : public overloads_base
  {
      overloads_common(char const* doc)
          : overloads_base(doc) {}

      overloads_common(char const* doc, keyword_range const& kw)
          : overloads_base(doc, kw) {}

      default_call_policies
      call_policies() const
      {
          return default_call_policies();
      }

      template <class CallPoliciesT>
      overloads_proxy<CallPoliciesT, DerivedT>
      operator[](CallPoliciesT const& policies) const
      {
          return overloads_proxy<CallPoliciesT, DerivedT>(
              policies, this->doc_string(), this->keywords());
      }
  };

}}} // namespace boost::python::detail


#define BOOST_PYTHON_TYPEDEF_GEN(z, index, data)                                \
    typedef typename ::boost::mpl::next<BOOST_PP_CAT(iter, index)>::type        \
        BOOST_PP_CAT(iter, BOOST_PP_INC(index));                                \
    typedef typename ::boost::mpl::apply0<BOOST_PP_CAT(iter, index)>::type      \
        BOOST_PP_CAT(T, index);

#define BOOST_PYTHON_FUNC_WRAPPER_GEN(z, index, data)                   \
    static RT BOOST_PP_CAT(func_,                                       \
        BOOST_PP_SUB_D(1, index, BOOST_PP_TUPLE_ELEM(3, 1, data))) (    \
        BOOST_PP_ENUM_BINARY_PARAMS_Z(                                  \
            1, index, T, arg))                                          \
    {                                                                   \
        BOOST_PP_TUPLE_ELEM(3, 2, data)                                 \
        BOOST_PP_TUPLE_ELEM(3, 0, data)(                                \
            BOOST_PP_ENUM_PARAMS(                                       \
                index,                                                  \
                arg));                                                  \
    }

#define BOOST_PYTHON_GEN_FUNCTION(fname, fstubs_name, n_args, n_dflts, ret)     \
    struct fstubs_name                                                          \
    {                                                                           \
        BOOST_STATIC_CONSTANT(int, n_funcs = BOOST_PP_INC(n_dflts));            \
        BOOST_STATIC_CONSTANT(int, max_args = n_funcs);                         \
                                                                                \
        template <typename SigT>                                                \
        struct gen                                                              \
        {                                                                       \
            typedef typename ::boost::mpl::begin<SigT>::type rt_iter;           \
            typedef typename ::boost::mpl::apply0<rt_iter>::type RT;            \
            typedef typename ::boost::mpl::next<rt_iter>::type iter0;           \
                                                                                \
            BOOST_PP_REPEAT_2ND(                                                \
                n_args,                                                         \
                BOOST_PYTHON_TYPEDEF_GEN,                                       \
                0)                                                              \
                                                                                \
            BOOST_PP_REPEAT_FROM_TO_2(                                          \
                BOOST_PP_SUB_D(1, n_args, n_dflts),                             \
                BOOST_PP_INC(n_args),                                           \
                BOOST_PYTHON_FUNC_WRAPPER_GEN,                                  \
                (fname, BOOST_PP_SUB_D(1, n_args, n_dflts), ret))               \
        };                                                                      \
    };                                                                          \

///////////////////////////////////////////////////////////////////////////////
#define BOOST_PYTHON_MEM_FUNC_WRAPPER_GEN(z, index, data)                       \
    static RT BOOST_PP_CAT(func_,                                               \
        BOOST_PP_SUB_D(1, index, BOOST_PP_TUPLE_ELEM(3, 1, data))) (            \
            ClassT obj BOOST_PP_COMMA_IF(index)                                 \
            BOOST_PP_ENUM_BINARY_PARAMS_Z(1, index, T, arg)                     \
        )                                                                       \
    {                                                                           \
        BOOST_PP_TUPLE_ELEM(3, 2, data) obj.BOOST_PP_TUPLE_ELEM(3, 0, data)(    \
            BOOST_PP_ENUM_PARAMS(index, arg)                                    \
        );                                                                      \
    }

#define BOOST_PYTHON_GEN_MEM_FUNCTION(fname, fstubs_name, n_args, n_dflts, ret) \
    struct fstubs_name                                                          \
    {                                                                           \
        BOOST_STATIC_CONSTANT(int, n_funcs = BOOST_PP_INC(n_dflts));            \
        BOOST_STATIC_CONSTANT(int, max_args = n_funcs + 1);                     \
                                                                                \
        template <typename SigT>                                                \
        struct gen                                                              \
        {                                                                       \
            typedef typename ::boost::mpl::begin<SigT>::type rt_iter;           \
            typedef typename ::boost::mpl::apply0<rt_iter>::type RT;            \
                                                                                \
            typedef typename ::boost::mpl::next<rt_iter>::type class_iter;      \
            typedef typename ::boost::mpl::apply0<class_iter>::type ClassT;     \
            typedef typename ::boost::mpl::next<class_iter>::type iter0;        \
                                                                                \
            BOOST_PP_REPEAT_2ND(                                                \
                n_args,                                                         \
                BOOST_PYTHON_TYPEDEF_GEN,                                       \
                0)                                                              \
                                                                                \
            BOOST_PP_REPEAT_FROM_TO_2(                                          \
                BOOST_PP_SUB_D(1, n_args, n_dflts),                             \
                BOOST_PP_INC(n_args),                                           \
                BOOST_PYTHON_MEM_FUNC_WRAPPER_GEN,                              \
                (fname, BOOST_PP_SUB_D(1, n_args, n_dflts), ret))               \
        };                                                                      \
    };

#define BOOST_PYTHON_OVERLOAD_CONSTRUCTORS(fstubs_name, n_args, n_dflts)        \
    fstubs_name(char const* doc = 0)                                            \
        : ::boost::python::detail::overloads_common<fstubs_name>(doc) {}        \
    template <class Keywords>                                                   \
    fstubs_name(char const* doc, Keywords const& keywords)                      \
        : ::boost::python::detail::overloads_common<fstubs_name>(               \
            doc, keywords.range())                                              \
    {                                                                           \
        typedef typename ::boost::python::detail::                              \
            error::more_keywords_than_function_arguments<                       \
                Keywords::size,n_args>::too_many_keywords assertion;            \
    }                                                                           \
    template <class Keywords>                                                   \
    fstubs_name(Keywords const& keywords, char const* doc = 0)                  \
        : ::boost::python::detail::overloads_common<fstubs_name>(               \
            doc, keywords.range())                                              \
    {                                                                           \
        typedef typename ::boost::python::detail::                              \
            error::more_keywords_than_function_arguments<                       \
                Keywords::size,n_args>::too_many_keywords assertion;            \
    }

# if defined(BOOST_NO_VOID_RETURNS)

#  define BOOST_PYTHON_GEN_FUNCTION_STUB(fname, fstubs_name, n_args, n_dflts)   \
    struct fstubs_name                                                          \
        : public ::boost::python::detail::overloads_common<fstubs_name>         \
    {                                                                           \
        BOOST_PYTHON_GEN_FUNCTION(                                              \
            fname, non_void_return_type, n_args, n_dflts, return)               \
        BOOST_PYTHON_GEN_FUNCTION(                                              \
            fname, void_return_type, n_args, n_dflts, ;)                        \
                                                                                \
        BOOST_PYTHON_OVERLOAD_CONSTRUCTORS(fstubs_name, n_args, n_dflts)        \
    };

#  define BOOST_PYTHON_GEN_MEM_FUNCTION_STUB(fname, fstubs_name, n_args, n_dflts)       \
    struct fstubs_name                                                                  \
        : public ::boost::python::detail::overloads_common<fstubs_name>                 \
    {                                                                                   \
        BOOST_PYTHON_GEN_MEM_FUNCTION(                                                  \
            fname, non_void_return_type, n_args, n_dflts, return)                       \
        BOOST_PYTHON_GEN_MEM_FUNCTION(                                                  \
            fname, void_return_type, n_args, n_dflts, ;)                                \
                                                                                        \
        BOOST_PYTHON_OVERLOAD_CONSTRUCTORS(fstubs_name, n_args, n_dflts)                \
    };

# else // !defined(BOOST_NO_VOID_RETURNS)

#  define BOOST_PYTHON_GEN_FUNCTION_STUB(fname, fstubs_name, n_args, n_dflts)   \
    struct fstubs_name                                                          \
        : public ::boost::python::detail::overloads_common<fstubs_name>         \
    {                                                                           \
        BOOST_PYTHON_GEN_FUNCTION(                                              \
            fname, non_void_return_type, n_args, n_dflts, return)               \
                                                                                \
        typedef non_void_return_type void_return_type;                          \
        BOOST_PYTHON_OVERLOAD_CONSTRUCTORS(fstubs_name, n_args, n_dflts)        \
    };


#  define BOOST_PYTHON_GEN_MEM_FUNCTION_STUB(fname, fstubs_name, n_args, n_dflts)       \
    struct fstubs_name                                                                  \
        : public ::boost::python::detail::overloads_common<fstubs_name>                 \
    {                                                                                   \
        BOOST_PYTHON_GEN_MEM_FUNCTION(                                                  \
            fname, non_void_return_type, n_args, n_dflts, return)                       \
                                                                                        \
        typedef non_void_return_type void_return_type;                                  \
        BOOST_PYTHON_OVERLOAD_CONSTRUCTORS(fstubs_name, n_args, n_dflts)                \
    };

# endif // !defined(BOOST_NO_VOID_RETURNS)

///////////////////////////////////////////////////////////////////////////////
//
//  MAIN MACROS
//
//      Given generator_name, fname, min_args and max_args, These macros
//      generate function stubs that forward to a function or member function
//      named fname. max_args is the arity of the function or member function
//      fname. fname can have default arguments. min_args is the minimum
//      arity that fname can accept.
//
//      There are two versions:
//
//          1. BOOST_PYTHON_FUNCTION_OVERLOADS for free functions
//          2. BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS for member functions.
//
//      For instance, given a function:
//
//      int
//      foo(int a, char b = 1, unsigned c = 2, double d = 3)
//      {
//          return a + b + c + int(d);
//      }
//
//      The macro invocation:
//
//          BOOST_PYTHON_FUNCTION_OVERLOADS(foo_stubs, foo, 1, 4)
//
//      Generates this code:
//
//      struct foo_stubsNonVoid
//      {
//          static const int n_funcs = 4;
//          static const int max_args = n_funcs;
//
//          template <typename SigT>
//          struct gen
//          {
//              typedef typename ::boost::mpl::begin<SigT>::type    rt_iter;
//              typedef typename rt_iter::type                      RT;
//              typedef typename rt_iter::next                      iter0;
//              typedef typename iter0::type                        T0;
//              typedef typename iter0::next                        iter1;
//              typedef typename iter1::type                        T1;
//              typedef typename iter1::next                        iter2;
//              typedef typename iter2::type                        T2;
//              typedef typename iter2::next                        iter3;
//              typedef typename iter3::type                        T3;
//              typedef typename iter3::next                        iter4;
//
//              static RT func_0(T0 arg0)
//              { return foo(arg0); }
//
//              static RT func_1(T0 arg0, T1 arg1)
//              { return foo(arg0, arg1); }
//
//              static RT func_2(T0 arg0, T1 arg1, T2 arg2)
//              { return foo(arg0, arg1, arg2); }
//
//              static RT func_3(T0 arg0, T1 arg1, T2 arg2, T3 arg3)
//              { return foo(arg0, arg1, arg2, arg3); }
//          };
//      };
//
//      struct foo_overloads
//          : public boost::python::detail::overloads_common<foo_overloads>
//      {
//          typedef foo_overloadsNonVoid    non_void_return_type;
//          typedef foo_overloadsNonVoid    void_return_type;
//
//          foo_overloads(char const* doc = 0)
//             : boost::python::detail::overloads_common<foo_overloads>(doc) {}
//      };
//
//      The typedefs non_void_return_type and void_return_type are
//      used to handle compilers that do not support void returns. The
//      example above typedefs non_void_return_type and
//      void_return_type to foo_overloadsNonVoid. On compilers that do
//      not support void returns, there are two versions:
//      foo_overloadsNonVoid and foo_overloadsVoid.  The "Void"
//      version is almost identical to the "NonVoid" version except
//      for the return type (void) and the lack of the return keyword.
//
//      See the overloads_common above for a description of the
//      foo_overloads' base class.
//
///////////////////////////////////////////////////////////////////////////////
#define BOOST_PYTHON_FUNCTION_OVERLOADS(generator_name, fname, min_args, max_args)          \
    BOOST_PYTHON_GEN_FUNCTION_STUB(                                                         \
        fname,                                                                              \
        generator_name,                                                                     \
        max_args,                                                                           \
        BOOST_PP_SUB_D(1, max_args, min_args))

#define BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(generator_name, fname, min_args, max_args)   \
    BOOST_PYTHON_GEN_MEM_FUNCTION_STUB(                                                     \
        fname,                                                                              \
        generator_name,                                                                     \
        max_args,                                                                           \
        BOOST_PP_SUB_D(1, max_args, min_args))

// deprecated macro names (to be removed)
#define BOOST_PYTHON_FUNCTION_GENERATOR BOOST_PYTHON_FUNCTION_OVERLOADS
#define BOOST_PYTHON_MEM_FUN_GENERATOR BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS

///////////////////////////////////////////////////////////////////////////////
#endif // DEFAULTS_GEN_JDG20020807_HPP


